#define M_PI 3.14159265358979323846

#define fmodp(x,n) ((n)*frac((x)/(n)))

#define ease(x) ((3*(x)*(x))-(2*(x)*(x)*(x)))

#define RandomRes 64

//McCool random nuber gernerator
float2 rand2(float2 ij)
{
  const float4 a=float4(M_PI * M_PI * M_PI * M_PI, exp(4.0), pow(13.0, M_PI / 2.0), sqrt(1997.0));
  float4 result =float4(ij,ij);

  for(int i = 0; i < 3; i++) 
  {
      result.x = frac(dot(result, a));
      result.y = frac(dot(result, a));
      result.z = frac(dot(result, a));
      result.w = frac(dot(result, a));
  }

  return float2(result.xy);
  //return float2(1,1);
}

float rand(float2 ij)
{
  const float4 a=float4(M_PI * M_PI * M_PI * M_PI, exp(4.0),  1.0, 0.0);
  const float4 b=float4(pow(13.0, M_PI / 2.0), sqrt(1997.0),  0.0, 1.0);

  float2 xy0    = ij/M_PI;
  float2 xym    = fmodp(xy0.xy,257.0)+1.0;
  float2 xym2   = frac(xym*xym);
  float4 pxy    = float4(xym.yx * xym2 , frac(xy0));
  float2 xy1    = float2(dot(pxy,a) , dot(pxy,b));
  float2 result = frac(xy1);
  
  return (result).x;
}


Texture3D txRandomNumbers;

SamplerState randomNumberSampler
{
	AddressU=Wrap;
	AddressV=Wrap;
	AddressW=Wrap;
};




float PerlinNoise2D(float2 input)
{	

	float dx=frac(input.x);
	float dy=frac(input.y);		



	
	float x0=(float)(floor(input.x))/RandomRes;
	float x1=(float)(ceil(input.x))/RandomRes;
	
	float y0=(float)(floor(input.y))/RandomRes;
	float y1=(float)(ceil(input.y))/RandomRes;
	
	
	
	
	float s=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y0,0));
	float t=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y1,0));
	float u=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y1,0));
	float v=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y0,0));
	


	
	float a=s+ease(dy)*(t-s);
	float b=v+ease(dy)*(u-v);
	
	
	float result=a+ease(dx)*(b-a);


		
	return result;
	
}

float PerlinNoise3D(float3 input)
{	

	float dx=frac(input.x);
	float dy=frac(input.y);	
	float dz=frac(input.z);		

	float x0=(float)(floor(input.x))/RandomRes;
	float x1=(float)(ceil(input.x))/RandomRes;
	
	float y0=(float)(floor(input.y))/RandomRes;
	float y1=(float)(ceil(input.y))/RandomRes;
	
	float z0=(float)(floor(input.z))/RandomRes;
	float z1=(float)(ceil(input.z))/RandomRes;
	
	
	
	
	float aaa=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y0,z0));
	float aba=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y1,z0));
	float baa=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y0,z0));
	float bba=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y1,z0));
	float aab=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y0,z1));
	float abb=txRandomNumbers.Sample(randomNumberSampler, float3(x0,y1,z1));
	float bab=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y0,z1));
	float bbb=txRandomNumbers.Sample(randomNumberSampler, float3(x1,y1,z1));
	


	
	float a=aaa+ease(dy)*(aba-aaa);
	float b=aab+ease(dy)*(abb-aab);
	
	float l=a+ease(dz)*(b-a);
	
	float c=baa+ease(dy)*(bba-baa);
	float d=bab+ease(dy)*(bbb-bab);
	
	float r=c+ease(dz)*(d-c);
	
	float result=l+ease(dx)*(r-l);


		
	return result;
	
}


float fbm2D(float2 input, float2 scale, int octaves)
{

	float result=0;
	float h=1;
	for(int i=0; i<octaves; i++)
	{
		result+=PerlinNoise2D(input*scale*h)/h;
		h*=2;
	}
	return result;
}

float fbm3D(float3 input, float3 scale, int octaves)
{

	float result=0;
	float h=1;
	for(int i=0; i<octaves; i++)
	{
		result+=PerlinNoise3D(input*scale*h)/h;
		h*=2;
	}
	return result;
}


float3 normalPerturbation(float3 normal, float2 position, float2 scale, float strength)
{
	float3 result;
	
	float3 d=float3(PerlinNoise2D(position*scale),
					PerlinNoise2D(position*scale+10.0f),
					PerlinNoise2D(position*scale+20.0f));
	
	result=(normal+strength*d)/(strength+1);

	return result;
}

float3 generateNormal(float2 position, float sampleResolution)
{
	float3 result;
	

	
	result=float3(1,0,0)*PerlinNoise2D((position+float2(sampleResolution,0)))+
	float3(-1,0,0)*PerlinNoise2D((position+float2(-sampleResolution,0)))+
	float3(0,0,-1)*PerlinNoise2D((position+float2(0,-sampleResolution)))+
	float3(0,0,1)*PerlinNoise2D((position+float2(0,sampleResolution)))+
	float3(0,1,0);
	
	result/=5;

	return result;
}

